/*


Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package controllers

import (
	"bytes"
	"context"
	"fmt"

	appsv1 "k8s.io/api/apps/v1"
	corev1 "k8s.io/api/core/v1"

	// "k8s.io/client-go/tools/record"

	"crypto/rand"
	"encoding/json"

	"crypto/tls"

	"net/http"

	"github.com/go-logr/logr"
	"k8s.io/apimachinery/pkg/runtime"
	ctrl "sigs.k8s.io/controller-runtime"
	"sigs.k8s.io/controller-runtime/pkg/client"
	"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
	"sigs.k8s.io/controller-runtime/pkg/event"
	"sigs.k8s.io/controller-runtime/pkg/handler"
	"sigs.k8s.io/controller-runtime/pkg/predicate"
	"sigs.k8s.io/controller-runtime/pkg/source"

	cmdbv1 "cmdb-controller-admin/api/v1"
)

var (
	i int
)

// CmdbhandleReconciler reconciles a Cmdbhandle object
type CmdbhandleReconciler struct {
	client.Client
	Log    logr.Logger
	Scheme *runtime.Scheme
	// Recorder record.EventRecorder
}

func updateDeployment(ctx context.Context, r *CmdbhandleReconciler, cmdbv1 *cmdbv1.Cmdbhandle, f *bytes.Buffer) error {

	log := r.Log.WithValues("func", "updateDeployment")

	deployment := appsv1.Deployment{}

	_ = json.Unmarshal(f.Bytes(), &deployment)

	if err := r.Update(ctx, &deployment); err != nil {
		log.Error(err, "update deployment error")
		return err
	}

	log.Info("update deployment success")

	return nil
}

// 新建deployment

func createDeployment(ctx context.Context, r *CmdbhandleReconciler, cmdbv1 *cmdbv1.Cmdbhandle, f *bytes.Buffer) error {
	log := r.Log.WithValues("func", "createDeployment")

	deployment := appsv1.Deployment{}

	_ = json.Unmarshal(f.Bytes(), &deployment)

	// 这一步非常关键！
	// 建立关联后，删除elasticweb资源时就会将deployment也删除掉
	log.Info("set reference")
	if err := controllerutil.SetControllerReference(cmdbv1, &deployment, r.Scheme); err != nil {
		log.Error(err, "SetControllerReference error")
		return err
	}

	// 创建deployment
	log.Info("start create deployment")
	if err := r.Create(ctx, &deployment); err != nil {
		return err
	}

	log.Info("create deployment success")

	return nil
}

type Config struct {
	CertFile string
	KeyFile  string
}

func httpClient(c *Config) (httpClient *http.Client) {
	cert, _ := tls.LoadX509KeyPair(c.CertFile, c.KeyFile)
	ssl := &tls.Config{
		Certificates:       []tls.Certificate{cert},
		InsecureSkipVerify: true,
	}
	ssl.Rand = rand.Reader
	return &http.Client{
		Transport: &http.Transport{
			TLSClientConfig: ssl,
		},
	}
}

/***检测状态
1 启动成功   检测不过关
2 启动成功   检测过关
3 启动失败   程序退出
4 调度失败    没有资源
*/

func checkStatusPod(Replicas *int32, cmdbv1 *cmdbv1.Cmdbhandle, pod *corev1.PodList) {
	cmdbv1.Status.CheckStatus.Pending = cmdbv1.Status.CheckStatus.Pending[:0]
	cmdbv1.Status.CheckStatus.Running_no = cmdbv1.Status.CheckStatus.Running_no[:0]
	cmdbv1.Status.CheckStatus.Running_yes = cmdbv1.Status.CheckStatus.Running_yes[:0]
	cmdbv1.Status.CheckStatus.CrashLoopBackOff = cmdbv1.Status.CheckStatus.CrashLoopBackOff[:0]

	// for i, pods := range pod.Items {
	// 	fmt.Println("=====", i, pods.Status.Phase)
	// 	// if pods.Status.Phase == "Pending" {
	// 	// 	fmt.Println("无法调度更新失败")
	// 	// 	cmdbv1.Status.CheckStatus.Pending = append(cmdbv1.Status.CheckStatus.Pending, pods.Name)
	// 	// 	continue
	// 	// }

	// 	for _, k := range pods.Status.ContainerStatuses {
	// 		if k.Name == cmdbv1.Name {
	// 			if k.State.Running != nil {
	// 				if k.Ready == false {
	// 					fmt.Println("健康检测中", k.State)
	// 					cmdbv1.Status.CheckStatus.Running_no = append(cmdbv1.Status.CheckStatus.Running_no, pods.Name)

	// 				}

	// 				if k.Ready == true {
	// 					fmt.Println("检测成功更新成功", k.State)
	// 					cmdbv1.Status.CheckStatus.Running_yes = append(cmdbv1.Status.CheckStatus.Running_yes, pods.Name)

	// 				}
	// 			}

	// 			if k.State.Waiting != nil {
	// 				if k.State.Waiting.Reason == "CrashLoopBackOff" {
	// 					fmt.Println("程序启动退出")
	// 					cmdbv1.Status.CheckStatus.CrashLoopBackOff = append(cmdbv1.Status.CheckStatus.CrashLoopBackOff, pods.Name)

	// 				}
	// 			}

	// 		}
	// 	}

	// }
	// if len(cmdbv1.Status.CheckStatus.CrashLoopBackOff) > 0 {
	// 	fmt.Println(("程序有问题退出"))
	// 	cmdbv1.Status.UpdateStatus = "false"
	// }
	// if len(cmdbv1.Status.CheckStatus.Running_no) > 0 {
	// 	fmt.Println(("程序检测不过关"))

	// 	cmdbv1.Status.UpdateStatus = "false"
	// }

	// if len(cmdbv1.Status.CheckStatus.Pending) > 0 {
	// 	fmt.Println(("程序无法调度了"))

	// 	cmdbv1.Status.UpdateStatus = "false"
	// }
	// if len(cmdbv1.Status.CheckStatus.Running_yes) > 0 {
	// 	fmt.Println(("程序检测没问题哦"))

	// 	if len(cmdbv1.Status.CheckStatus.Running_yes) == int(*Replicas) {
	// 		cmdbv1.Status.UpdateStatus = "true"

	// 	}

	// }
	// fmt.Println("============更新状态", cmdbv1.Status.UpdateStatus)

	// time.Sleep(2 * time.Second)

}

// +kubebuilder:rbac:groups=cmdb.cmdb.com,resources=cmdbhandles,verbs=get;list;watch;create;update;patch;delete
// +kubebuilder:rbac:groups=cmdb.cmdb.com,resources=cmdbhandles/status,verbs=get;update;patch
// +kubebuilder:rbac:groups=apps,resources=deployments,verbs=get;list;watch;create;update;patch;delete
// +kubebuilder:rbac:groups=core,resources=services,verbs=get;list;watch;create;update;patch;delete
// +kubebuilder:rbac:groups="",resources=pods,verbs=get;list;watch;create;update;patch;delete

type ResourceLabelChangedPredicate struct {
	predicate.Funcs
}

// Create implements Predicate
// func (p *ResourceLabelChangedPredicate) Create(e event.CreateEvent) bool {
// 	fmt.Println("Create==")

// 	return false

// 	// if p.CreateFunc != nil {
// 	// 	return p.CreateFunc(e)
// 	// }
// 	// return true
// }

func (p *ResourceLabelChangedPredicate) Delete(e event.DeleteEvent) bool {
	instance := &cmdbv1.Cmdbhandle{}

	fmt.Println("Delete==1", instance)
	return false

	// if p.DeleteFunc != nil {
	// 	fmt.Println("Delete==2", e)

	// 	return p.DeleteFunc(e)
	// }
	// return true
}

func (rl *ResourceLabelChangedPredicate) Update(e event.UpdateEvent) bool {
	oldobj, ok1 := e.ObjectOld.(*cmdbv1.Cmdbhandle)
	newobj, ok2 := e.ObjectNew.(*cmdbv1.Cmdbhandle)
	fmt.Println("===update")
	if ok1 == true && ok2 == true && ok1 == ok2 {
		fmt.Println("===========cmdb", ok1, ok2, oldobj.Name, oldobj.Kind, newobj.Name, newobj.Kind)

		return true
	}
	_, ok1 = e.ObjectOld.(*corev1.Pod)
	_, ok2 = e.ObjectNew.(*corev1.Pod)
	if ok1 == true && ok2 == true && ok1 == ok2 {
		fmt.Println("===========pod", ok1, ok2, oldobj.Name, oldobj.Kind, newobj.Name, newobj.Kind)

		return true

	}

	return false
}

//判断你这个资源变更了  之后转发给控制器true转发false不转发

func (r *CmdbhandleReconciler) Reconcile(req ctrl.Request) (ctrl.Result, error) {
	// var (
	// 	err  error
	// 	data []byte
	// 	f    bytes.Buffer
	// 	tmpl *template.Template
	// )
	_ = context.Background()
	_ = r.Log.WithValues("Reconcile", req.NamespacedName)
	// instance := &cmdbv1.Cmdbhandle{}
	// 服务启动时候回进行一次回调
	// 查询Cmdbhandle是否存在
	// fmt.Println("21111111111111", req.Name, req.Namespace, req.NamespacedName, err)

	// if err = r.Get(ctx, req.NamespacedName, instance); err != nil {
	// 	// 如果没有实例，就返回空结果，这样外部就不再立即调用Reconcile方法了 delete的时候回进入到这个逻辑中
	// 	if errors.IsNotFound(err) {
	// 		fmt.Println("2===========================", req.Name, req.Namespace, req.NamespacedName, err)
	// 		log.Info("cmdbhandle not found, maybe removed")
	// 		return reconcile.Result{}, nil
	// 	}
	// 	log.Error(err, "")

	// 	return ctrl.Result{}, err

	// }

	// if data, err = ioutil.ReadFile("/Users/mac/go/src/cmdb-controller-admin/template/java/dep.json"); err != nil {
	// 	log.Error(err, "template read FAIL")

	// }
	// if tmpl, err = template.New("dep").Parse(string(data)); err != nil {
	// 	log.Error(err, "template uncode FAIL")

	// }

	// instance.Spec.K8s_name = instance.Name
	// tmpl.Execute(&f, instance.Spec)

	// deployment := &appsv1.Deployment{}

	// 查询Deployment是否存在

	// if err = r.Get(ctx, req.NamespacedName, deployment); err != nil {
	// 	if errors.IsNotFound(err) {
	// 		//   没有deployment则创建
	// 		fmt.Println("2===========================", req.Name, req.Namespace, req.NamespacedName, err)

	// 		if err = createDeployment(ctx, r, instance, &f); err != nil {
	// 			log.Error(err, "createDeployment  FAIL")
	// 			return ctrl.Result{}, err
	// 		}

	// 		//创建svc ing hpa

	// 	}

	// }

	// var index int
	// for i, d := range deployment.Spec.Template.Spec.Containers {
	// 	if d.Name == instance.Name {
	// 		index = i
	// 		break
	// 	}
	// }

	// 如果条件相等，就直接返回了

	// if deployment.Spec.Template.Spec.Containers[index].Image == instance.Spec.Image && instance.Status.UpdateStatus == "true" {
	// 	return ctrl.Result{}, nil

	// }

	// 如果不等，就要调整
	// deployment.Spec.Template.Spec.Containers[0].Image = instance.Spec.Image

	// 通过客户端更新deployment
	// instance.Status.UpdateStatus = "false"
	// if err = r.Update(ctx, deployment); err != nil {
	// 	log.Error(err, "update deployment replicas error")
	// 	// 返回错误信息给外部
	// 	return ctrl.Result{}, err
	// }

	// err = r.Get(ctx, req.NamespacedName, deployment)
	//更新更新状态

	// pod := &corev1.Pod{}

	// l, _ := labels.Parse("app=" + instance.Name)
	// opt := &client.ListOptions{
	// 	LabelSelector: l,
	// }
	// if err = r.List(ctx, pod, opt); err != nil {
	// 	log.Error(err, "error")
	// 	return ctrl.Result{}, err

	// }

	// if err = r.Get(ctx, req.NamespacedName, pod); err != nil {
	// 	log.Error(err, "error")
	// 	return ctrl.Result{}, err

	// }
	i += 1
	// for _, v := range pod.Items {

	// fmt.Println("1===========================", err, v.Name, v.Annotations)
	// ds, _ := json.MarshalIndent(v.Status.Conditions, "", "    ")
	// ds2, _ := json.MarshalIndent(v.Status.ContainerStatuses, "", "    ")

	fmt.Println("START===========================")
	// fmt.Println(string(ds))
	// fmt.Println(string(ds2))
	fmt.Println(req.Name, req.Namespace, req.NamespacedName)
	// fmt.Println(v.Status.Conditions)
	// fmt.Println(v.Status.ContainerStatuses)

	fmt.Println("END===========================")

	//  v.Annotations, v.Finalizers, v.Labels, v.Namespace, v.Status)

	// }

	// checkStatusPod(deployment.Spec.Replicas, instance, pod)

	// s1 := deployment.Status.Conditions[0].Status
	// s2 := deployment.Status.Conditions[1].Status
	// dep_rep := *deployment.Spec.Replicas
	// dep_rep_status := deployment.Status.UnavailableReplicas
	// Reason := deployment.Status.Conditions[1].Reason
	// if err != nil {
	// 	return ctrl.Result{}, err
	// }

	// if Reason == "NewReplicaSetAvailable" && dep_rep_status == 0 {
	// 	fmt.Println("===状态更新1", s1, s2, Reason, dep_rep_status, dep_rep, deployment.Spec.Template.Spec.Containers[0].Image, instance.Spec.Image)

	// 	fmt.Println("更新完毕", deployment.Name, *deployment.Spec.Replicas)
	// 	instance.Status.UpdateStatus = true
	// 	if err := r.Update(ctx, instance); err != nil {
	// 		log.Error(err, "update instance error")
	// 	}
	// 	return ctrl.Result{}, nil

	// }
	// fmt.Println("更新中", instance.Status.UpdateStatus)
	// if err := r.Update(ctx, instance); err != nil {
	// 	log.Error(err, "update instance error")
	// }

	// return ctrl.Result{}, nil
	return ctrl.Result{}, nil

}

func (r *CmdbhandleReconciler) SetupWithManager(mgr ctrl.Manager) error {
	return ctrl.NewControllerManagedBy(mgr).
		//4 6 5 7
		For(&cmdbv1.Cmdbhandle{}).
		Owns(&appsv1.Deployment{}).
		Watches(&source.Kind{Type: &corev1.Pod{}}, &handler.EnqueueRequestForObject{}).
		WithEventFilter(&ResourceLabelChangedPredicate{}).
		Complete(r)

}

// c := &Config{}
// c.CertFile = "/etc/kube-config/test/client-cert.pem"
// c.KeyFile = "/etc/kube-config/test/client-key.pem"
// httpClient := httpClient(c)
// url := "https://172.16.0.223:6443"
// url += "/apis/apps/v1/namespaces/default/deployments"
// // reqest.Header.Add("Cookie", "xxxxxx")
// reqest, _ := http.NewRequest("POST", url, &f)
// reqest.Header.Add("Content-Type", "application/yaml")
// resp, err := httpClient.Do(reqest)
// fmt.Println("status", err)

// body, err := ioutil.ReadAll(resp.Body)
// m := make(map[string]interface{})

// fmt.Println("sss", f.String())
// json.Unmarshal(body, &m)
// dss, _ := json.MarshalIndent(&m, "", "    ")
// fmt.Println("=====", string(dss))

// 	res = http.request('POST', url, headers={
// 		'': 'application/json'}, body=body)
// print res.data
